﻿#region Copyright (c) 2008 plexBB Team
/*  Copyright (c) 2008 plexBB Team
 *  
 *  This file is free software: you may copy, redistribute and/or modify it  
 *  under the terms of the GNU General Public License as published by the  
 *  Free Software Foundation, either version 2 of the License, or (at your  
 *  option) any later version.  
 *  
 *  This file is distributed in the hope that it will be useful, but  
 *  WITHOUT ANY WARRANTY; without even the implied warranty of  
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  
 *  General Public License for more details.  
 *  
 *  You should have received a copy of the GNU General Public License  
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */
#endregion

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using PlexBB.Data;
using PlexBB.Install;

namespace PlexBB
{
    /// <summary>
    /// Represents data and actions available for all components of PlexBB.
    /// It has similar lifecycle as <see cref="System.Web.HttpContext"/> class.
    /// </summary>
    public class PlexBBContext : IDisposable
    {
        private const string _httpContextKey = "PlexBB.Context";
        private static bool _installed;
        private static readonly object _installLock = new object();
        private readonly PlexBB _plexBB;
        private readonly PlexBBConfigurationSection _configuration;
        private readonly HttpContext _httpContext;
        private readonly PlexBBData _data;
        private readonly PlexBBActionInfo _requestAction;
        private User _user;

        /// <summary>
        /// To be used only at PlexBB control constructor.
        /// </summary>
        internal PlexBBContext(PlexBB plexBB)
        {
            if (HttpContext.Current.Items.Contains(_httpContextKey))
            {
                throw new InvalidOperationException();
            }

            _plexBB = plexBB;
            _httpContext = HttpContext.Current;
            _configuration = (PlexBBConfigurationSection)ConfigurationManager.GetSection("plexBB");

            // install if required
            // using double check design pattern
            if (!_installed)
            {
                lock (_installLock)
                {
                    if (!_installed)
                    {
                        PlexBBInstaller installer = new PlexBBInstaller(_configuration);
                        installer.InstallIfRequired();
                        _installed = true;
                    }
                }
            }

            _data = new PlexBBData(_configuration.ConnectionString, _configuration.DatabaseTablePrefix);
            // create _requestAction
            {
                Dictionary<string, string> currentParams = new Dictionary<string, string>();
                string prefix = _configuration.UrlParamPrefix;
                foreach (string key in _httpContext.Request.QueryString)
                {
                    if (prefix == string.Empty || key.StartsWith(prefix))
                    {
                        currentParams.Add(key.Substring(prefix.Length), _httpContext.Request.QueryString[key]);
                    }
                }
                _requestAction = new PlexBBActionInfo(currentParams);
            }

            HttpContext.Current.Items[_httpContextKey] = this;
        }

        public static PlexBBContext Current
        {
            get { return (PlexBBContext)HttpContext.Current.Items[_httpContextKey]; }
        }

        public PlexBB PlexBB
        {
            get { return _plexBB; }
        }

        public Page Page
        {
            get { return _plexBB.Page; }
        }

        public Control ActionControl
        {
            get { return _plexBB.ActionControl; }
        }

        public Module CurrentModule
        {
            get
            {
                IModuleContainer moduleContainer = _plexBB.ActionControl as IModuleContainer;
                return moduleContainer != null ? moduleContainer.CurrentModule : null;
            }
        }

        public PlexBBConfigurationSection Configuration
        {
            get { return _configuration; }
        }

        public PlexBBData Data
        {
            get { return _data; }
        }

        public PlexBBActionInfo RequestAction
        {
            get { return _requestAction; }
        }

        public User User
        {
            get
            {
                if (_user == null)
                {
                    _user = new User();
                }
                return _user;
                //                return new Data.User();// Data.Users.Single(u => u.Name == "anonymous");
            }
        }

        internal string GetResourceServerPath(string relativeResourcePath)
        {
            return Path.Combine(_httpContext.Server.MapPath(_configuration.BaseDir), relativeResourcePath);
        }

        #region CreateControl
        //public ForumTemplate CreateControl(Type controlType)
        //{
        //    // load languages before loading control 
        //    Language lang = User.Lang;
        //    foreach (RequiredLangFilesAttribute attribute in controlType.GetCustomAttributes(typeof(RequiredLangFilesAttribute), true))
        //    {
        //        foreach (string langFile in attribute.LangFiles)
        //        {
        //            lang.Require(langFile);
        //        }
        //    }

        //    bool adminTemlate = controlType.Namespace == "PlexBB.Templates.Admin";
        //    string controlPath = string.Format("{0}/{1}/{2}.ascx",
        //                                       Configuration.BaseDir.TrimEnd('/'),
        //                                       adminTemlate ? "Resources/AdminStyle/Templates" : "Styles/Prosilver/Templates",
        //                                       controlType.Name);
        //    return (ForumTemplate)Page.LoadControl(controlPath);
        //}

        //public T CreateControl<T>() where T : ForumTemplate
        //{
        //    return (T)CreateControl(typeof(T));
        //}
        #endregion

        #region CreateUrl
        public string CreateUrl(string action)
        {
            return CreateUrl(action, null);
        }

        public string CreateUrl(string action, string param1Name, object param1Value)
        {
            return CreateUrl(action, new Dictionary<string, object>() { { param1Name, param1Value } });
        }

        public string CreateUrl(string action, string param1Name, object param1Value, string param2Name, object parame2Value)
        {
            return CreateUrl(action, new Dictionary<string, object>() { { param1Name, param1Value }, { param2Name, parame2Value } });
        }

        public string CreateUrl(string action, string param1Name, object param1Value, string param2Name, object parame2Value, string param3Name, object param3Value)
        {
            return CreateUrl(action, new Dictionary<string, object>() { { param1Name, param1Value }, { param2Name, parame2Value }, { param3Name, param3Value } });
        }

        public string CreateUrl(string action, IDictionary<string, object> parameters)
        {
            // do not allow null or empty action
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }
            if (action.Length == 0)
            {
                throw new ArgumentException("action");
            }

            // determine what actions to use what handler
            bool useRawView;
            switch (action)
            {
                case "acp":
                    useRawView = true;
                    break;
                default:
                    useRawView = false;
                    break;
            }

            HttpServerUtility server = _httpContext.Server;
            string encodedPrefix = server.UrlEncode(Configuration.UrlParamPrefix);
            string forumUrlResolved;

            if (useRawView)
            {
                forumUrlResolved = Page.ResolveUrl("./RawView.aspx");
            }
            else
            {
                forumUrlResolved = Page.ResolveUrl(Configuration.ForumUrl.Length == 0 ? "./" : Configuration.ForumUrl);
            }

            StringBuilder url = new StringBuilder(forumUrlResolved);
            if (forumUrlResolved.IndexOf('?') == -1)
            {
                url.Append('?');
            }
            char lastChar = url[url.Length - 1];
            if (lastChar != '&' && lastChar != '?')
            {
                url.Append('&');
            }
            url.Append(encodedPrefix);
            url.Append("act=");
            url.Append(server.UrlEncode(action));

            if (parameters != null)
            {
                foreach (KeyValuePair<string, object> pair in parameters)
                {
                    url.Append('&');
                    url.Append(encodedPrefix);
                    url.Append(server.UrlEncode(pair.Key));
                    url.Append('=');
                    url.Append(server.UrlEncode(Convert.ToString(pair.Value)));
                }
            }
            return url.ToString();
        }

        #endregion

        public void Dispose()
        {
            _data.Dispose();
        }
    }

    public class PlexBBActionInfo
    {
        private readonly IDictionary<string, string> _parameters;

        public PlexBBActionInfo(IDictionary<string, string> parameters)
        {
            _parameters = parameters;
        }

        public string Name
        {
            get
            {
                string name;
                _parameters.TryGetValue("act", out name);
                return name;
            }
        }

        public string this[string paramName]
        {
            get
            {
                string value;
                _parameters.TryGetValue(paramName, out value);
                return value;
            }
        }
    }
}
